/*-
 * SMSSubmitReport.java
 *
 * $LastChangedBy: stein $
 * $LastChangedRevision: 98 $
 * $LastChangedDate: 2007-02-20 15:18:26 +0000 (Tue, 20 Feb 2007) $
 * $HeadURL: svn://localhost/SMS/src/com/googlecode/attention/sms/pdu/SMSSubmitReport.java $
 *
 * Copyright (c) 2007, Stein Gunnar Bakkeby
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Stein Gunnar Bakkeby nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.googlecode.attention.sms.pdu;

import java.util.EnumSet;

import com.googlecode.attention.util.IntArrayBuilder;
import com.googlecode.attention.util.KeepingReference;

import static com.googlecode.attention.sms.pdu.PDUElement.*;

/** * SMS-SUBMIT-REPORT. * <BR> * <BR>An SMS-SUBMIT-REPORT TPDU is carried as a RP-User-Data element within * an RP-ERROR PDU and is part of the negative acknowledgement to an SMS-SUBMIT * or SMS-COMMAND. * <BR> * <BR>An SMS-SUBMIT-REPORT TPDU is also carried as a RP-User-Data element with * an RP-ACK PDU and is part of a positive acknowledgement to a SMS-SUBMIT or * SMS-COMMAND. * <BR> * <BR><B>(i) SMS-SUBMIT-REPORT for RP-ERROR</B> * <BR> * <BR>Basic elements of the SMS-SUBMIT-REPORT type: * <BR> * <BR> * <TABLE BORDER="1"> * <TR><TD>Abbr.</TD><TD>Reference</TD><TD>P1)</TD><TD>P2)</TD><TD>Description</TD></TR> * <TR><TD>TP-MTI</TD><TD>TP-Message-Type-Indicator</TD><TD>M</TD><TD>2b</TD><TD>Parameter describing the message type</TD></TR> * <TR><TD>TP-UDHI</TD><TD>TP-User-Data-Header-Indication</TD><TD>O</TD><TD>b</TD><TD>Parameter indicating that the TP-UD field contains a Header</TD></TR> * <TR><TD>TP-FCS</TD><TD>TP-Failure-Cause</TD><TD>M</TD><TD>I</TD><TD>Parameter indicating the reason for SMS-SUBMIT failure</TD></TR> * <TR><TD>TP-PI</TD><TD>TP-Parameter-Indicator</TD><TD>M</TD><TD>o</TD><TD>Parameter indicating the presence of any of the optional parameters which follow</TD></TR> * <TR><TD>TP-SCTS</TD><TD>TP-Service-Centre-Time-Stamp</TD><TD>M</TD><TD>7o<BR>5)</TD><TD>Parameter identifying the time when the SC received the SMS-SUBMIT</TD></TR> * <TR><TD>TP-PID</TD><TD>TP-Protocol-Identifier</TD><TD>O</TD><TD>o</TD><TD>See PID</TD></TR> * <TR><TD>TP-DCS</TD><TD>TP-Data-Coding-Scheme</TD><TD>O</TD><TD>o</TD><TD>See DCS</TD></TR> * <TR><TD>TP-UDL</TD><TD>TP-User-Data-Length</TD><TD>O</TD><TD>o</TD><TD>See UD.getUDL()</TD></TR> * <TR><TD>TP-UD</TD><TD>TP-User-Data</TD><TD>O</TD><TD>3) 4)</TD><TD>See UD</TD></TR> * </TABLE> * <BR> * <BR>1) Provision: Mandatory (M) or Optional (O). * <BR>2) Representation: Integer (I), bit (b), 2bits (2b), octet (o). * <BR>3) Dependent upon the TP-DCS. * <BR>4) The TP-User-Data field in the SMS-SUBMIT-REPORT is only available for use by the SC. * <BR>5) This same time value shall also be carried in the SMS-STATUS-REPORT * relating to a particular SM. This shall allow the submitting SME to * associate a particular SMS-SUBMIT with a subsequent SMS-STATUS-REPORT by * correlating the TP-SCTS values. * <BR> * <BR>Bits 7 and 5 - 2 in octet 1 are presently unused and the sender shall * set them to zero. If any of these bits is non-zero, the receiver shall not * examine the other field and shall treat the TP-Failure-Cause as * "Unspecified error cause". * <BR> * <BR> * <BR><B>(ii) SMS-SUBMIT-REPORT for RP-ACK</B> * <BR> * <BR>Basic elements of the SMS-SUBMIT_REPORT type: * <BR> * <BR> * <TABLE BORDER="1"> * <TR><TD>Abbr</TD><TD>Reference</TD><TD>P1)</TD><TD>P2)</TD><TD>Description</TD></TR> * <TR><TD>TP-MTI</TD><TD>TP-Message Type-Indicator</TD><TD>M</TD><TD>2b</TD><TD>Parameter describing the message type</TD></TR> * <TR><TD>TP-UDHI</TD><TD>TP-User-Data-Header-Indication</TD><TD>O</TD><TD>b</TD><TD>Parameter indicating that the TP-UD field contains a Header</TD></TR> * <TR><TD>TP-PI</TD><TD>TP-Parameter-Indicator</TD><TD>M</TD><TD>o</TD><TD>Parameter indicating the presence of any of the optional parameters which follow</TD></TR> * <TR><TD>TP-SCTS</TD><TD>TP-Service-Centre-Time-Stamp</TD><TD>M</TD><TD>7o<BR>5)</TD><TD>Parameter identifying the time when the SC received the SMS-SUBMIT</TD></TR> * <TR><TD>TP-PID</TD><TD>TP-Protocol-Identifier</TD><TD>O</TD><TD>o</TD><TD>See PID</TD></TR> * <TR><TD>TP-DCS</TD><TD>TP-Data-Coding-Scheme</TD><TD>O</TD><TD>o</TD><TD>See DCS</TD></TR> * <TR><TD>TP-UDL</TD><TD>TP-User-Data-Length</TD><TD>O</TD><TD>o</TD><TD>See UD.getUDL()</TD></TR> * <TR><TD>TP-UD</TD><TD>TP-User-Data</TD><TD>O</TD><TD>3) 4)</TD><TD>See UD</TD></TR> * </TABLE> * <BR> * <BR>1) Provision: Mandatory (M) or Optional (O). * <BR>2) Representation: Integer (I), Bit (B), 2bits (2b), octet (o). * <BR>3) Dependent upon the TP-DCS. * <BR>4) The TP-User-Data field in the SMS-SUBMIT-REPORT is only available for use by the SC. * <BR>5) This same time value shall also be carried in the SMS-STATUS-REPORT * relating to a particular SM. This shall allow the submitting SME to * associate a particular SMS-SUBMIT with a subsequent SMS-STATUS-REPORT by * correlating the TP-SCTS values. * <BR> * <BR>Bits 7 and 5 - 2 in octet 1 are presently unused in the * SMS-SUBMIT-REPORT and the sender shall set them to zero. If any of these * bits is non-zero, the receiver shall ignore them. *  * @author Stein Gunnar Bakkeby */
public class SMSSubmitReport extends TPDU {
  /* Field values */
  private boolean ack;
  private int  fo   = 0;    // first octet
  private int  fcs  = 0;    // failure cause (only for TP-ERROR)
  private int  pi   = 0;    // parameter indicator
  private SCTS scts = null; // scts
  private int  pid  = 0;    // optional
  private int  dcs  = 0;    // optional

  private final EnumSet<PDUElement> pduElementSet = EnumSet.of(TP_FO, TP_MTI,
      TP_UDHI, TP_PI, TP_SCTS, TP_PID, TP_DCS, TP_UDL, TP_UD, TP_UDH);
  private int extensionOctet;

  /**
   * Constructor, creates an empty SMS-SUBMIT-REPORT.
   * 
   * @param ack True if this is an RP-ACK, false if RP-ERROR
   */
  public SMSSubmitReport(final boolean ack) {
    super(new int[1], TPDU.SMS_SUBMIT_REPORT);
    this.ack = ack;
  }
  
  /**
   * Constructor, creates the SMS-SUBMIT-REPORT based upon the given PDU.
   * 
   * @param tpdu The raw data
   * @param ack True if this is an RP-ACK, false if RP-ERROR
   */
  public SMSSubmitReport(final String tpdu, final boolean ack) {
    super(tpdu, TPDU.SMS_SUBMIT_REPORT);
    this.ack = ack;
  }
  
  /**
   * Constructor, creates the SMS-SUBMIT-REPORT based upon the given PDU.
   * 
   * @param tpdu The raw data
   * @param ack True if this is an RP-ACK, false if RP-ERROR
   */
  public SMSSubmitReport(final int[] tpdu, final boolean ack) {
    super(tpdu, TPDU.SMS_SUBMIT_REPORT);
    this.ack = ack;
  }
  
  /**
   * Constructor, will create the SMS-SUBMIT-REPORT as an RP-ERROR.
   * 
   * @param fo The first octet of this SMS-SUBMIT-REPORT.
   * @param fcs The Failure-Cause
   * @param pi The Parameter-Indicator
   * @param scts The Service-Centre-Time-Stamp
   * @param pid The Protocol-Identifier
   * @param dcs The Data-Coding-Scheme
   * @param ud The User-Data
   */
  public SMSSubmitReport(final FO fo,
                         final FCS fcs,
                         final PI pi,
                         final SCTS scts,
                         final PID pid,
                         final DCS dcs,
                         final UD ud) {
    super(new int[1], TPDU.SMS_SUBMIT_REPORT);
    this.fo   = fo.getValue();
    this.fcs  = fcs.getValue();
    this.pi   = pi.getValue();
    this.scts = scts;
    this.pid  = pid.getValue();
    this.dcs  = dcs.getValue();
    this.ud   = ud;
    this.ack  = false;
  }
  
  /**
   * Constructor, will create the SMS-SUBMIT-REPORT as an RP-ACK.
   * 
   * @param fo The first octet of this SMS-SUBMIT-REPORT.
   * @param pi The Parameter-Indicator
   * @param scts The Service-Centre-Time-Stamp
   * @param pid The Protocol-Identifier
   * @param dcs The Data-Coding-Scheme
   * @param ud The User-Data
   */
  public SMSSubmitReport(final FO fo,
                         final PI pi,
                         final SCTS scts,
                         final PID pid,
                         final DCS dcs,
                         final UD ud) {
    super(new int[1], TPDU.SMS_SUBMIT_REPORT);
    this.fo   = fo.getValue();
    this.pi   = pi.getValue();
    this.scts = scts;
    this.pid  = pid.getValue();
    this.dcs  = dcs.getValue();
    this.ud   = ud;
    this.ack  = false;
  }
  
  /**
   * Constructor, will create the SMS-SUBMIT-REPORT an an RP-ERROR without
   * the optional PID, DCS, and UD.
   * 
   * @param fo The first octet of this SMS-SUBMIT-REPORT.
   * @param fcs The Failure-Cause
   * @param pi The Parameter-Indicator
   * @param scts The Service-Centre-Time-Stamp
   */
  public SMSSubmitReport(final FO fo,
                         final FCS fcs,
                         final PI pi,
                         final SCTS scts) {
    super(new int[1], TPDU.SMS_SUBMIT_REPORT);
    this.fo   = fo.getValue();
    this.fcs  = fcs.getValue();
    this.pi   = pi.getValue();
    this.scts = scts;
    this.ack  = false;
  }
  
  /**
   * Constructor, will create the SMS-SUBMIT-REPORT an an RP-ACK without
   * the optional PID, DCS, and UD.
   * 
   * @param fo The first octet of this SMS-SUBMIT-REPORT.
   * @param pi The Parameter-Indicator
   * @param scts The Service-Centre-Time-Stamp
   */
  public SMSSubmitReport(final FO fo, final PI pi, final SCTS scts) {
    super(new int[1], TPDU.SMS_SUBMIT_REPORT);
    this.fo   = fo.getValue();
    this.pi   = pi.getValue();
    this.scts = scts;
    this.ack  = false;
  }
  
  /**
   * This will return true if the PDU contains the given type.
   *
   * @param type PDUElement values
   * @see PDUElement
   * @return True if given type is present, false otherwise
   */
  public boolean contains(final PDUElement type) {
    return pduElementSet.contains(type);
  }
  
  /**
   * This will return an element of a given type.
   * 
   * @param type PDUElement values
   * @see PDUElement
   * @return The element in this TPDU
   */
  public int get(final PDUElement type) {
    switch (type) {
    case TP_FO:   return fo;
    case TP_MTI:  return fo & TP_MTI.value;
    case TP_UDHI: return fo & TP_UDHI.value;
    case TP_PI:   return pi;
    case TP_PID:  return pid;
    case TP_DCS:  return dcs;
    case TP_UDL:  return ud.getUDL();
      
    default:
      return -1;
    }
  }
  
  /**
   * This will return the service centre time stamp.
   * 
   * @return The SCTS of this submit report.
   */
  public SCTS getServiceCentreTimeStamp() {
    return scts;
  }

  /**
   * This will set an element of a given type.
   * 
   * @param type PDUElement values
   * @see PDUElement
   * @param value The value of the element to set
   * @return True if PDU contains the given type, false otherwise
   */
  public boolean set(final PDUElement type, final int value) {
    switch (type) {
    case TP_FO:
      fo  = value;
      break;
      
    case TP_PI:
      pi  = value;
      break;
      
    case TP_PID:
      pid = value;
      break;
    
    case TP_DCS:
      dcs = value;
      break;
      
    case TP_MTI:
    case TP_UDHI:
      final FO f = new FO(fo);
      f.set(type, value);
      fo = f.getValue();
      break;
    
    default:
      return false;
    }
    return true;
  }
  
  /**
   * This will set an element of a given type.
   * 
   * @param type PDUElement values
   * @see PDUElement
   * @param value The value of the element to set
   * @return True if PDU contains the given type, false otherwise
   */
  public boolean set(final PDUElement type, final int[] value) {
    switch (type) {
    case TP_SCTS:
      scts = new SCTS(value);
      return true;

    case TP_UD:
      ud = new UD(value, -1, UDHI.parse(fo), new DCS(dcs));
      return true; // XXX udl?
    
    default:
      return false;
    }
  }
  
  /**
   * This will set the service centre time stamp.
   * 
   * @param scts The SCTS of this submit report.
   */
  @KeepingReference
  public void setServiceCentreTimeStamp(final SCTS scts) {
    this.scts = scts;
  }

  /**
   * Set this to true to indicate RP-ACK, or set it to false to indicate
   * that this is an RP-ERROR.
   * 
   * @param value True if RP-ACK, false if RP-ERROR
   */
  public void setAck(final boolean value) {
    this.ack = value;
  }

  /**
   * This will return true if this is an RP-ACK, false if it is an RP-ERROR.
   * 
   * @return True if RP-ACK, false otherwise
   * @see #isError()
   */
  public boolean isAck() {
    return ack;
  }
  
  /**
   * This will return true if this is an RP-ERROR, false if it is an RP-ACK.
   * 
   * @return True if RP-ERROR, false otherwise
   * @see #isAck()
   */
  public boolean isError() {
    return !ack;
  }
  
  /**
   * This will decode the SMS-SUBMIT-REPORT.
   */
  public void decode() {
    resetPDUPointer();
    fo  = extractOctet();
    fcs = (ack ? 0 : extractOctet());
    pi  = extractOctet();
    final PI thisPI = new PI(pi);
    if (thisPI.isExtensionOctetIndicated()) {
      /* Extension octet is not defined, so ignore this octet. */
      extensionOctet = extractOctet();
    }
    scts = new SCTS(extractOctets(7));
    pid = thisPI.isPIDIndicated() ? extractOctet() : 0;
    dcs = thisPI.isDCSIndicated() ? extractOctet() : 0;
    if (thisPI.isUDLIndicated()) {
      int udl = extractOctet();
      if (udl > 0) {
        ud = new UD(extractOctets(udl), udl, UDHI.parse(fo), new DCS(dcs));
      }
    }
    if (thisPI.isReservedIndicated()) {
      /* Ignore */
    }
  }
  
  /**
   * This will encode the SMS-SUBMIT-REPORT.
   */
  public void encode() {
    /* Avoid NullPointerException */
    if (scts == null) scts = new SCTS();
    if (ud   == null) ud   = new UD();
    
    final IntArrayBuilder output = new IntArrayBuilder(INIT_SIZE);
    
    output.append(fo);
    if (!ack) output.append(fcs);
    output.append(this.pi);
    final PI pi = new PI(this.pi);

    if (pi.isExtensionOctetIndicated()) {
      output.append(extensionOctet); // Unknown extension octet
    }
    output.append(scts.getVP());
    if (pi.isPIDIndicated()) output.append(pid);
    if (pi.isDCSIndicated()) output.append(dcs);
    if (pi.isUDLIndicated()) {
      output.append(ud.getUDL());
      output.append(ud);
    }
    if (pi.isReservedIndicated()) {
      /* Ignore */
    }
    if (rest != null && rest.length != 0) {
      output.append(rest);
    }
    super.pdu = output;
  }
  
  /**
   * This is an SMS report and therefore does not have an originators address
   * or a destination address. Because of this the method will return null.
   * 
   * @return null
   */
  @Override
  public Address getAddress() {
    return null;
  }
  
  /**
   * This is an SMS report and therefore does not have an originators address
   * or a destination address. Thus this the method will ignore the given
   * argument.
   * 
   * @param address ignored
   */
  @Override
  public void setAddress(final Address address) {}
  
  /**
   * @return [FO:&lt;fo&gt;][FCS:&lt;fcs&gt;][PI:&lt;pi&gt;][SCTS:&lt;scts&gt;][PID:&lt;pid&gt;][DCS:&lt;dcs&gt;][UD:&lt;ud&gt;]
   */
  public String toString() {
    this.decode();
    final StringBuilder sb = new StringBuilder(256);
    sb.append("[FO:");
    sb.append(fo);
    if (!ack) {
      sb.append("][FCS:");
      sb.append(fcs);
    }
    sb.append("][PI:");
    sb.append(pi);
    sb.append("][SCTS:"); 
    sb.append(scts);
    final PI pi = new PI(this.pi);
    if (pi.isPIDIndicated()) {
      sb.append("][PID:");
      sb.append(pid);
    }
    if (pi.isDCSIndicated()) {
      sb.append("][DCS:");
      sb.append(dcs);
    }
    if (pi.isUDLIndicated()) {
      sb.append("][UD:");
      sb.append(ud);
    }
    if (pi.isReservedIndicated()) {
      /* Ignore */
    }
    sb.append(']');
    return sb.toString();
  }
}